package com.dxtx.widget.imageedit;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;

import com.dxtx.common.R;
import com.dxtx.util.SimpleAnimListener;

import java.util.ArrayList;
import java.util.List;

//选取,裁剪方形图像的控件,兼备放大查看功能
public class RectEditImageView extends FrameLayout {
    private Bitmap bitmap;
    private RectF mViewBounds, dst, firstRect;
    private Paint clearPaint, rectBoundsPaint;

    // 矩形选区的边界
    private RectF selectRect;

    // 最大放大倍数
    private float max_scale = 2.0f;

    // 双指的距离
    private float mDistance;
    private float x0, y0;
    private boolean doublePoint;

    private int r = (int) getResources().getDimension(R.dimen._10dp);

    private int mWidth, mHeight;

    private float mScale = 1f;

    public RectEditImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public RectEditImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RectEditImageView(Context context) {
        super(context);
        init();
    }

    private void init() {
        detector = new GestureDetector(getContext(), gestureListener);

        clearPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        clearPaint.setColor(Color.GRAY);
        clearPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
        rectBoundsPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        rectBoundsPaint.setColor(Color.WHITE);
        rectBoundsPaint.setStyle(Style.STROKE);
        rectBoundsPaint.setStrokeWidth(getResources().getDimension(R.dimen._1dp));

        for (int i = 0; i < points.length; i++) {
            points[i] = new WhitePoint(getContext());
            points[i].getView().setBackgroundResource(R.drawable.white_point);
            addView(points[i].getView(), r * 2, r * 2);
        }
        // 每个点处理相应的边界
        points[0].addOnTouchListener(lListener);
        points[1].addOnTouchListener(lListener);
        points[2].addOnTouchListener(lListener);

        points[3].addOnTouchListener(rListener);
        points[4].addOnTouchListener(rListener);
        points[5].addOnTouchListener(rListener);

        points[0].addOnTouchListener(tListener);
        points[6].addOnTouchListener(tListener);
        points[3].addOnTouchListener(tListener);

        points[2].addOnTouchListener(bListener);
        points[7].addOnTouchListener(bListener);
        points[5].addOnTouchListener(bListener);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {

        if (bitmap != null) {
            canvas.drawBitmap(bitmap, null, dst, null);
        }
        // 保存图层,以免清除时清除了bitmap
        int count = canvas.saveLayer(mViewBounds, null, Canvas.ALL_SAVE_FLAG);
        canvas.drawColor(0x80000000);
        // 清除半透明黑色,留下一个透明圆
        canvas.drawRect(selectRect, clearPaint);
        canvas.restoreToCount(count);

        canvas.drawRect(selectRect, rectBoundsPaint);
        super.dispatchDraw(canvas);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        initStates(w, h);
    }

    private void initStates(int w, int h) {
        // 记录view所占的矩形
        mViewBounds = new RectF(0, 0, w, h);
        float l, r, t, b;
        if (bitmap != null) {
            w -= this.r * 2;
            h -= this.r * 2;
            // 判断长宽比,当长宽比太长会太宽是,以fitCenter的方式初始化图片
            int srcWidth = bitmap.getWidth() - this.r * 2;
            int srcHeight = bitmap.getHeight() - this.r * 2;
            if (w / (float) h > srcWidth / (float) srcHeight) {
                // 图片太高,则改变应用到的实际宽度
                float w_ = h * srcWidth / (float) srcHeight;
                /*l = w / 2f - w_ / 2f;
                r = w / 2f + w_ / 2f;
                t = 0;
                b = h;*/
                l = w / 2f - w_ / 2f + this.r;
                r = w / 2f + w_ / 2f + this.r;
                t = this.r;
                b = h - this.r;
            } else {
                // 图片太长,或跟view一样长
                float h_ = w * srcHeight / (float) srcWidth;
                l = this.r;
                r = w - this.r;
                t = h / 2f - h_ / 2f + this.r;
                b = h / 2f + h_ / 2f + this.r;
            }
            dst = new RectF(l, t, r, b);// 这个矩形用来变换
            firstRect = new RectF(l, t, r, b);// 这个矩形仅为保存第一次的状态

            max_scale = Math.max(max_scale, srcWidth / firstRect.width());
        }
        selectRect = new RectF(firstRect);

        // 分布8个点

        layoutPoints();
    }

    private void layoutPoints() {
        // 左边线上三点
        points[0].setX(selectRect.left - r);
        points[0].setY(selectRect.top - r);

        points[1].setX(selectRect.left - r);
        points[1].setY((selectRect.top + selectRect.bottom) / 2 - r);

        points[2].setX(selectRect.left - r);
        points[2].setY(selectRect.bottom - r);

        // 右边线上三点
        points[3].setX(selectRect.right - r);
        points[3].setY(selectRect.top - r);

        points[4].setX(selectRect.right - r);
        points[4].setY((selectRect.top + selectRect.bottom) / 2 - r);

        points[5].setX(selectRect.right - r);
        points[5].setY(selectRect.bottom - r);

        // 上下 线的中点
        points[6].setX((selectRect.right + selectRect.left) / 2 - r);
        points[6].setY(selectRect.top - r);

        points[7].setX((selectRect.right + selectRect.left) / 2 - r);
        points[7].setY(selectRect.bottom - r);

        // 那么 改变top的有0,6,3 改变bottom的有 2,7,5

    }

    public void setImageBitmap(Bitmap bitmap) {
        this.bitmap = bitmap;
        if (mWidth != 0 && mHeight != 0) {//第二次
            initStates(mWidth, mHeight);
        }
        invalidate();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent e) {
        boolean duper = super.dispatchTouchEvent(e);
        if (duper)
            return true;

        detector.onTouchEvent(e);

        if (e.getPointerCount() > 2) {// 不接受多于两指的事件
            return false;
        } else if (e.getPointerCount() == 2) {
            doublePoint = true;// 标志位,记录两指事件处理后,抬起一只手也不处理拖动
            handleDoubleMove(e);
        } else {
            // 处理单指的拖动
            switch (e.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    x0 = e.getX();
                    y0 = e.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (doublePoint) {
                        break;
                    }
                    float x = e.getX();
                    float y = e.getY();
                    float w = dst.width();
                    float h = dst.height();

                    // 不允许拖过圆形区域,不能使圆形区域内空白
                    dst.left += x - x0;
                    if (dst.left > selectRect.left) {
                        dst.left = selectRect.left;
                    } else if (dst.left < selectRect.right - w) {
                        dst.left = selectRect.right - w;
                    }
                    dst.right = dst.left + w;

                    // 不允许拖过圆形区域,不能使圆形区域内空白
                    dst.top += y - y0;
                    if (dst.top > selectRect.top) {
                        dst.top = selectRect.top;
                    } else if (dst.top < selectRect.bottom - h) {
                        dst.top = selectRect.bottom - h;
                    }
                    dst.bottom = dst.top + h;

                    x0 = x;
                    y0 = y;
                    invalidate();
                    break;
                case MotionEvent.ACTION_UP:
                    doublePoint = false;// 恢复标志位
                    break;
            }
        }

        return true;
    }

    // 处理双指事件
    private void handleDoubleMove(MotionEvent e) {
        switch (e.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_POINTER_DOWN:
                mDistance = getDistance(e);
//                Log.d("px", "down:distance=" + mDistance);
                break;
            case MotionEvent.ACTION_MOVE:
                scale(e);
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
    }

    private void scale(MotionEvent e) {
        float dis = getDistance(e);
        // 以双指中心作为图片缩放的支点
        float pX = e.getX(0) / 2f + e.getX(1) / 2f;
        float pY = e.getY(0) / 2f + e.getY(1) / 2f;
        float scale = dis / mDistance;

        float w = dst.width();
        float h = dst.height();

        if (w * scale < selectRect.width() || h * scale < selectRect.height() || w * scale > firstRect.width() * max_scale) {
            // 无法缩小到比选区还小,或到达最大倍数
            return;
        }
        // 把dst区域放大scale倍
        dst.left = (dst.left - pX) * scale + pX;
        dst.right = (dst.right - pX) * scale + pX;
        dst.top = (dst.top - pY) * scale + pY;
        dst.bottom = (dst.bottom - pY) * scale + pY;

        // 缩放同样不允许使圆形区域空白
        if (dst.left > selectRect.left) {
            dst.left = selectRect.left;
            dst.right = dst.left + w * scale;
        } else if (dst.right < selectRect.right) {
            dst.right = selectRect.right;
            dst.left = dst.right - w * scale;
        }

        if (dst.top > selectRect.top) {
            dst.top = selectRect.top;
            dst.bottom = dst.top + h * scale;
        } else if (dst.bottom < selectRect.bottom) {
            dst.bottom = selectRect.bottom;
            dst.top = dst.bottom - h * scale;
        }
        invalidate();

        mDistance = dis;
        mScale = dst.width() / firstRect.width();
    }

    private float getDistance(MotionEvent e) {
        return (float) Math.sqrt((e.getX(0) - e.getX(1)) * (e.getX(0) - e.getX(1)) + (e.getY(0) - e.getY(1)) * (e.getY(0) - e.getY(1)));
    }

    // 生成目前选区指定大小的矩形bitmap
    public Bitmap extractBitmap() {
        int width = (int) (selectRect.width() / dst.width() * bitmap.getWidth());
        int height = (int) (selectRect.height() / dst.height() * bitmap.getHeight());

        Bitmap outBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);

        Canvas canvas = new Canvas(outBitmap);

        float scale = dst.width() / bitmap.getWidth();

        int l = (int) ((selectRect.left - dst.left) / scale);
        int r = (int) ((selectRect.right - dst.left) / scale);
        int t = (int) ((selectRect.top - dst.top) / scale);
        int b = (int) ((selectRect.bottom - dst.top) / scale);

        Rect resRect = new Rect(l, t, r, b);
        canvas.drawBitmap(bitmap, resRect, canvas.getClipBounds(), null);
        return outBitmap;
    }

    private WhitePoint[] points = new WhitePoint[8];

    class WhitePoint {
        View view;
        List<OnTouchListener> list = new ArrayList<OnTouchListener>();

        public WhitePoint(Context context) {
            view = new TextView(context);
            view.setOnTouchListener(new OnTouchListener() {

                @Override
                public boolean onTouch(View v, MotionEvent e) {
                    boolean hasChange = false;

                    for (OnTouchListener l : list) {// 修改边界的处理者
                        hasChange = l.onTouch(v, e) || hasChange;
                    }
                    if (hasChange) {
                        layoutPoints();// 重新分布是为了让其他点联动
                        invalidate();// 重绘所有界面
                    }
                    return true;
                }
            });
        }

        public void setX(float x) {
            view.setX(x);
        }

        public void setY(float y) {
            view.setY(y);
        }

        public View getView() {
            return view;
        }

        public void setView(TextView view) {
            this.view = view;
        }

        public void addOnTouchListener(OnTouchListener l) {
            list.add(l);
        }

    }

    private OnTouchListener lListener = new OnTouchListener() {
        private float x;

        @Override
        public boolean onTouch(View v, MotionEvent e) {
            switch (e.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    x = e.getRawX();
                    break;
                case MotionEvent.ACTION_MOVE:
                    // 计算应该拖动到的地方
                    float x_ = e.getRawX() - x + v.getX();

                    // 分辨是否越界图片,或者太小
                    if (x_ > dst.left - r && selectRect.right - x_ >= r * 4) {
                        v.setX(x_);
                        selectRect.left = x_ + r;
                        x = x_;
                        return true;
                    } else {
                        return false;
                    }
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    break;
                default:
                    break;
            }
            return false;
        }

    };

    private OnTouchListener rListener = new OnTouchListener() {
        private float x;

        @Override
        public boolean onTouch(View v, MotionEvent e) {
            switch (e.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    x = e.getRawX();
                    break;
                case MotionEvent.ACTION_MOVE:
                    // 计算应该拖动到的地方
                    float x_ = e.getRawX() - x + v.getX();
                    if (x_ < dst.right - r && x_ - selectRect.left >= r * 4) {
                        v.setX(x_);
                        selectRect.right = x_ + r;
                        x = x_;
                        return true;
                    } else {
                        return false;
                    }
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    break;
                default:
                    break;
            }
            return false;
        }

    };

    private OnTouchListener tListener = new OnTouchListener() {
        private float y;

        /**
         * return 是否改变了
         */
        @Override
        public boolean onTouch(View v, MotionEvent e) {
            switch (e.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    y = e.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    // 计算应该拖动到的地方
                    float y_ = e.getRawY() - y + v.getY();
                    if (y_ > dst.top - r && selectRect.bottom - y_ >= r * 4) {
                        v.setY(y_);
                        selectRect.top = y_ + r;
                        y = y_;
                        return true;
                    } else {
                        return false;
                    }
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    break;
                default:
                    break;
            }
            return false;
        }

    };

    private OnTouchListener bListener = new OnTouchListener() {
        private float y;

        /**
         * return 是否改变了
         */
        @Override
        public boolean onTouch(View v, MotionEvent e) {
            switch (e.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    y = e.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    // 计算应该拖动到的地方
                    float y_ = e.getRawY() - y + v.getY();
                    if (y_ <= dst.bottom - r && y_ - selectRect.top >= r * 4) {
                        v.setY(y_);
                        selectRect.bottom = y_ + r;
                        y = y_;
                        return true;
                    } else {
                        return false;
                    }
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    break;
                default:
                    break;
            }
            return false;
        }
    };

    private GestureDetector detector;
    //缩放过渡动画
    private ValueAnimator animator;
    private GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            //以该点作为缩放的中心
            float pX = e.getX();
            float pY = e.getY();

            float w = dst.width();
            float h = dst.height();

            final float toScale;

            if (mScale < 1) {//当前缩放值很小
                toScale = 1f;
            } else if (mScale >= max_scale) {//当前缩放值已经达到最大,则直接缩小,否则都是放大
                toScale = 1f;
            } else if (max_scale > 2f) {//最大超过2,分三个层次,max-->2-->1;
                //当前缩放值在1(包括1)到最大值之间
                toScale = mScale >= 2f ? max_scale : 2f;
            } else {//最大=2 只有两个层次 2-->1
                toScale = max_scale;
            }
//            Log.d("px", "max_scale=" + max_scale + ",mScale=" + mScale + ",toScale=" + toScale);

            //已不存在双击无法缩放的说法,最大的时候会缩小
            final RectF from = new RectF(dst);
            final RectF to = new RectF();

            if (toScale == 1) {
                to.set(firstRect);
            } else {
                float scale = toScale / mScale;//把dst区域放大scale倍,是指放大到当前大小的scale倍

                // 把dst区域放大scale倍
                to.left = (dst.left - pX) * scale + pX;
                to.right = (dst.right - pX) * scale + pX;
                to.top = (dst.top - pY) * scale + pY;
                to.bottom = (dst.bottom - pY) * scale + pY;

                // 缩放同样不允许使圆形区域空白
                if (to.left > selectRect.left) {
                    to.left = selectRect.left;
                    to.right = to.left + w * scale;
                } else if (dst.right < selectRect.right) {
                    to.right = selectRect.right;
                    to.left = to.right - w * scale;
                }

                if (to.top > selectRect.top) {
                    to.top = selectRect.top;
                    to.bottom = to.top + h * scale;
                } else if (dst.bottom < selectRect.bottom) {
                    to.bottom = selectRect.bottom;
                    to.top = to.bottom - h * scale;
                }
            }
            animator = ValueAnimator.ofFloat(0, 1);
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float v = (Float) animation.getAnimatedValue();
                    dst.left = (to.left - from.left) * v + from.left;
                    dst.top = (to.top - from.top) * v + from.top;
                    dst.right = (to.right - from.right) * v + from.right;
                    dst.bottom = (to.bottom - from.bottom) * v + from.bottom;
                    invalidate();
                }
            });
            animator.addListener(new SimpleAnimListener() {
                @Override
                public void onAnimationCancel(Animator animation) {
//                    Log.d("px", "onAnimationCancel");
                    dst.set(to);
                    mScale = toScale;
                    invalidate();
                }

                @Override
                public void onAnimationEnd(Animator animation) {
//                    Log.d("px", "onAnimationEnd to scale=" + toScale);
                    mScale = toScale;
                }
            });
            animator.setDuration(500);
            animator.start();
//            mScale = dst.width() / firstRect.width();
            return true;
        }
    };
}
